﻿using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.Domain.Repositories;
using Abp.Linq.Extensions;
using Abp.Organizations;
using Abp.Runtime.Session;
using Abp.UI;
using Abp.Net.Mail;
using Abp.Localization;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Partner.Surround.Authorization;
using Partner.Surround.Organizations.Dto;
using Partner.Surround.Configuration;

namespace Partner.Surround.Organizations
{
    /// <summary>
    /// 组织机构应用层服务
    /// </summary>
    public class OrganizationUnitAppService : SurroundAppServiceBase, IOrganizationUnitAppService
    {
        #region 初始化
        private readonly OrganizationUnitManager _organizationUnitManager;
        private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
        private readonly IRepository<UserOrganizationUnit, long> _userOrganizationUnitRepository;

        public OrganizationUnitAppService(
            OrganizationUnitManager organizationUnitManager,
            IRepository<OrganizationUnit, long> organizationUnitRepository,
            IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository)
        {
            _organizationUnitManager = organizationUnitManager;
            _organizationUnitRepository = organizationUnitRepository;
            _userOrganizationUnitRepository = userOrganizationUnitRepository;
        }
        #endregion

        #region 组织机构
        public async Task<ListResultDto<OrganizationUnitListDto>> GetAllOrganizationUnitTree()
        {
            var organizationUnits = await _organizationUnitRepository.GetAllListAsync();
            return new ListResultDto<OrganizationUnitListDto>(ObjectMapper.Map<List<OrganizationUnitListDto>>(organizationUnits));
        }

        public async Task<PagedResultDto<OrganizationUnitListDto>> GetPagedOrganizationUnit(GetPagedOrganizationUnitInput input)
        {
            var organizationUnitQuery = _organizationUnitRepository.GetAll();
            if (input.Id.HasValue)
            {
                var parentOrganizationUnit = await _organizationUnitRepository.GetAsync(input.Id.Value);
                organizationUnitQuery = organizationUnitQuery.Where(o => o.Code.StartsWith(parentOrganizationUnit.Code));
            }

            var query = from ou in organizationUnitQuery
                        join uou in _userOrganizationUnitRepository.GetAll() on ou.Id equals uou.OrganizationUnitId into g
                        select new
                        {
                            ou,
                            memberCount = g.Count()
                        };

            var totalCount = await query.CountAsync();
            var items = await query.PageBy(input).ToListAsync();

            return new PagedResultDto<OrganizationUnitListDto>(
                totalCount,
                items.Select(item =>
                {
                    var dto = ObjectMapper.Map<OrganizationUnitListDto>(item.ou);
                    dto.MemberCount = item.memberCount;
                    return dto;
                }).ToList());
        }

        public async Task<GetOrganizationUnitForEditOutput> GetOrganizationUnitForEdit(NullableIdDto<long> input)
        {
            var output = new GetOrganizationUnitForEditOutput()
            {
                OrganizationUnit = new OrganizationUnitEditDto()
            };

            if (input.Id.HasValue && input.Id.Value > 0)
            {
                var organizationUnits = await _organizationUnitRepository.GetAsync(input.Id.Value);
                output.OrganizationUnit = ObjectMapper.Map<OrganizationUnitEditDto>(organizationUnits);
            }

            return output;
        }

        public async Task CreateOrUpdateOrganizationUnit(CreateOrUpdateOrganizationUnitInput input)
        {
            if (!input.OrganizationUnit.Id.HasValue)
            {
                await CreateOrganizationUnit(input);
            }
            else
            {
                await UpdateOrganizationUnit(input);
            }
        }

        /// <summary>
        /// 创建组织机构
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AbpAuthorize(PermissionNames.Pages_Frame_OrganizationUnits_Create)]
        private async Task CreateOrganizationUnit(CreateOrUpdateOrganizationUnitInput input)
        {
            var organizationUnit = new OrganizationUnit(AbpSession.TenantId, input.OrganizationUnit.DisplayName, input.OrganizationUnit.ParentId);
            await _organizationUnitManager.CreateAsync(organizationUnit);
            await CurrentUnitOfWork.SaveChangesAsync();
        }

        /// <summary>
        /// 更新组织机构
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AbpAuthorize(PermissionNames.Pages_Frame_OrganizationUnits_Update)]
        private async Task UpdateOrganizationUnit(CreateOrUpdateOrganizationUnitInput input)
        {
            var organizationUnit = await _organizationUnitRepository.FirstOrDefaultAsync(input.OrganizationUnit.Id.Value);

            if (organizationUnit == null)
                throw new UserFriendlyException("指定机构不存在");

            organizationUnit.DisplayName = input.OrganizationUnit.DisplayName;

            await _organizationUnitManager.UpdateAsync(organizationUnit);
        }

        [AbpAuthorize(PermissionNames.Pages_Frame_OrganizationUnits_Update)]
        public async Task<OrganizationUnitListDto> MoveOrganizationUnit(MoveOrganizationUnitInput input)
        {
            await _organizationUnitManager.MoveAsync(input.Id, input.NewParentId);
            var organizationUnit = await _organizationUnitRepository.GetAsync(input.Id);

            var dto = ObjectMapper.Map<OrganizationUnitListDto>(organizationUnit);
            dto.MemberCount = await _userOrganizationUnitRepository.CountAsync(uou => uou.OrganizationUnitId == organizationUnit.Id);

            return dto;
        }

        [AbpAuthorize(PermissionNames.Pages_Frame_OrganizationUnits_Delete)]
        public async Task DeleteOrganizationUnit(List<EntityDto<long>> inputs)
        {
            foreach (var input in inputs)
            {
                await _organizationUnitManager.DeleteAsync(input.Id);
            }
        }
        #endregion
    }
}
